/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.bht.esa.weather.wettercomrestclient;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import de.bht.esa.weather.model.Forecast;
import de.bht.esa.weather.model.WetterComForecast;
import de.bht.esa.weather.model.WetterComLocation;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.core.MediaType;
import org.apache.commons.codec.binary.Hex;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

/**
 * Stellt die Verbduing zum REST-Service von Wetter.com her.
 * 
 * @author Andreas Goese
 * @author Sven Höche
 */
public class WetterComRESTClient {
    private WebResource webResource;
    private Client client;
    private static final String BASE_URI = "http://api.wetter.com";
    private String apiKey;
    private String projectName;
    private MessageDigest md5;

    
    /**
     * Initialisiert den REST-Client mit dem von Wetter.com bereitgestellten API-Key, sowieso dem festgelegten Projektnamen
     * 
     * @param apiKey Der von Wetter.com bereitgestellte API-Key
     * @param projectName Der Name des bei Wetter.com angelegten Projekts
     * @throws Exception 
     */
    public WetterComRESTClient(String apiKey, String projectName) throws Exception{
        ClientConfig config = new DefaultClientConfig();
        client = Client.create(config);
        webResource = client.resource(BASE_URI);
        
        if(projectName == null || projectName.equals("")){
            throw new IllegalArgumentException(this.getClass().getName() + ": Missing or empty Argument: projectName");
        }
        
        if(apiKey == null || apiKey.equals("")){
            throw new IllegalArgumentException(this.getClass().getName() + ": Missing or empty Argument: apiKey");
        }
        
        this.projectName = projectName;
        this.apiKey = apiKey;
        
        this.md5 = MessageDigest.getInstance("MD5");
    }
    
    /**
     * Fragt die zum Suchstring passenden Orte bei Wetter.com an.
     * 
     * @param query Der Suchstring (z.B. Berlin oder 13353)
     * @return Array mit den gefunden Orten.
     * @throws Exception 
     */
    public WetterComLocation[] getLocations(String query) throws Exception{
        if(query == null || query.equals("")){
            throw new IllegalArgumentException(this.getClass().getName() + "::getLocations: Missing or empty Argument: query");
        }
        
        String resultString = this.webResource.path("location").path("index")
                        .path("search").path(query)
                        .path("project").path(this.projectName)
                        .path("cs").path(this.generateChecksum(query))
                        .path("output").path("json")
                        .accept(MediaType.APPLICATION_JSON)
                        .get(String.class);
        
        if(resultString == null || resultString.equals("")){
            throw new IllegalStateException(this.getClass().getName() + "::getLocations: Request to Wetter.com Service failed: no result!");
        }
        
        JSONObject tmpRootObj = new JSONObject(resultString);
        JSONArray tmpSearchResultArr = tmpRootObj.getJSONObject("search").getJSONArray("result");
        
        WetterComLocation[] result = new WetterComLocation[tmpSearchResultArr.length()];
        
        for(int i = 0; i < tmpSearchResultArr.length(); i++){
            JSONObject tmpSearchResultElement = tmpSearchResultArr.getJSONObject(i);
            result[i] = new WetterComLocation();
            result[i].setCityCode(tmpSearchResultElement.getString("city_code"));
            result[i].setName(tmpSearchResultElement.getString("name"));
            result[i].setZip(tmpSearchResultElement.getString("plz"));
            result[i].setDistrict(tmpSearchResultElement.getString("quarter"));
            result[i].setCountry(tmpSearchResultElement.getString("adm_1_name"));
            result[i].setState(tmpSearchResultElement.getString("adm_2_name"));
            result[i].setCounty(tmpSearchResultElement.getString("adm_4_name"));
        }
        return result;
    }
    /**
     * Gibt das Wetter für einen Ort, anhand des zum Ort passenden City-Codes zurück. Den City-Code erhält man bei der Location-Suche.
     * 
     * @param cityCode City-Code des Ortes für den der Wetterbericht angefragt werden soll. Den City-Code erhält man bei der Location-Suche.
     * @return Den abgefragten Wetterbericht
     * @throws Exception 
     */
    public WetterComForecast getForecast(String cityCode) throws Exception{
        //TestURL: http://api.wetter.com/forecast/weather/city/DE0001020/project/bhtesaweatherdemo/cs/2b31ad3f031a3518b682d7308405f2ed
        if(cityCode == null || cityCode.equals("")){
            throw new IllegalArgumentException(this.getClass().getName() + "::getForecast: Missing or empty Argument: cityCode");
        }
        
        String resultString = this.webResource.path("forecast").path("weather")
                .path("city").path(cityCode)
                .path("project").path(this.projectName)
                .path("cs").path(this.generateChecksum(cityCode))
                .path("output").path("json")
                .accept(MediaType.APPLICATION_JSON)
                .get(String.class);
        Logger.getLogger(this.getClass().getName()).log(Level.WARNING,this.generateChecksum(cityCode));
        
        WetterComForecast resultForecast = new WetterComForecast();
        
        JSONObject tmpRootObj = new JSONObject(resultString);
        JSONObject tmpCityObj = tmpRootObj.getJSONObject("city");
        resultForecast.setDetailUrl(tmpCityObj.getString("url"));
        
        Logger LOG = Logger.getAnonymousLogger();
        
        JSONObject forecastRootObj = tmpCityObj.getJSONObject("forecast");
        String key = (String)forecastRootObj.keys().next();
        
        JSONObject tmpForecast = forecastRootObj.getJSONObject(key);
        
        Forecast dayForecast = new Forecast();
        dayForecast.setLocalTimestamp(Long.parseLong(tmpForecast.getString("d")));
        dayForecast.setUtcTimestamp(Long.parseLong(tmpForecast.getString("du")));
        dayForecast.setPredictionPeriod(Double.parseDouble(tmpForecast.getString("p")));
        dayForecast.setWeatherTxt(tmpForecast.getString("w_txt"));
        dayForecast.setWeatherCode(Integer.parseInt(tmpForecast.getString("w")));
        dayForecast.setRainChance(Double.parseDouble(tmpForecast.getString("pc")));
        dayForecast.setWindDirection(Double.parseDouble(tmpForecast.getString("wd")));
        dayForecast.setWindDirectionText(tmpForecast.getString("wd_txt"));
        dayForecast.setWindSpeed(Double.parseDouble(tmpForecast.getString("ws")));
        dayForecast.setMinTemp(Double.parseDouble(tmpForecast.getString("tn")));
        dayForecast.setMaxTemp(Double.parseDouble(tmpForecast.getString("tx")));
        
        resultForecast.setForecast(dayForecast);

        return resultForecast;
    }
    
    /**
     * Generiert die Checksumme, die für die Anfragen bei Wetter.com benötigt wird (siehe Wetter.com-API Dokumentation: http://www.wetter.com/apps_und_mehr/website/api/dokumentation/)
     * 
     * @param query String für den die Checksumme genriert werden soll
     * @return Die Checksumme
     * @throws Exception 
     */
    protected String generateChecksum(String query) throws Exception{
        query = this.projectName + this.apiKey + query;
        this.md5.reset();
        this.md5.update(query.getBytes(Charset.forName("UTF8")));
        final byte[] resultByte = this.md5.digest();
        final String result = new String(Hex.encodeHex(resultByte));
        
        return result;
    }
    
    
}
